﻿#include "precomp.h"
#include "jflyrecord.h"
#include "jsqldatamgr.h"
#include "jsqltablemgr.h"
#include "jruledict.h"
#include <QSqlDatabase>
#include <QSqlQuery>
#include "jprecitimer.h"
#include "sqlite/sqlite3.h"

// - class JFlyRecordPrivate -

class JFlyRecordPrivate
{
    J_DECLARE_PUBLIC(JFlyRecord)
    public:
        JFlyRecordPrivate(JFlyRecord *parent)
      : q_ptr(parent)
      , dataMgr(0)
    {

    }

private:
    JSqlDataMgr *dataMgr;
};

// - class JFlyRecord -

J_IMPLEMENT_SINGLE_INSTANCE(JFlyRecord)

JFlyRecord::JFlyRecord() :
    d_ptr(new JFlyRecordPrivate(this))
{
    Q_D(JFlyRecord);
    d->dataMgr = JSqlDataMgr::instance();
}

JFlyRecord::~JFlyRecord()
{
    delete d_ptr;
}

bool JFlyRecord::parseFileName(const QString &filePath, QString &JX)
{
    QString fileName = QFileInfo(filePath).fileName();
    if (fileName.isEmpty()) {
        qCritical() << QStringLiteral("文件名称不能为空！");
        return false;
    }

    // get section of model_number
    QString text = fileName.section('-', 0, 0);

    if (text.count() != 5) {
        qCritical() << QStringLiteral("文件名格式无效！（机型和机号信息长度无效[%1]）[%2]")
                       .arg(fileName).arg(text);
        return false;
    }

    // 机型
    JX = text.left(3);

    return true;
}

bool JFlyRecord::parseFileName(const QString &filePath, JFileNameInfo &info)
{
    QString fileName = QFileInfo(filePath).fileName();
    if (fileName.isEmpty()) {
        qCritical() << QStringLiteral("文件名称不能为空！");
        return false;
    }

    // get section of model_number
    QString text = fileName.section('-', 0, 0);
    if (text.count() != 5) {
        qCritical() << QStringLiteral("文件名格式无效！（机型和机号信息长度无效[%1]）[%2]")
                       .arg(fileName).arg(text);
        return false;
    }

    // 机型
    info.JX = text.left(3);

    // 机号
    info.JH = text.right(text.count() - 3);

    // 日期
    info.minRQ = QDate::fromString(fileName.section('-', 1, 1), "yyyyMMdd");
    if (!info.minRQ.isValid()) {
        qCritical() << QStringLiteral("文件名格式无效！(日期无效)[%1]").arg(fileName);
        return false;
    }

    text = fileName.section('-', 2, -1);

    // 架次
    QString strJC = text.section('_', 0, 0);
    bool ok = false;
    info.JC = strJC.toInt(&ok);
    if (!ok) {
        qCritical() << QStringLiteral("文件名格式无效！(架次无效)[%1]").arg(fileName);
        return false;
    }

    return true;
}

bool JFlyRecord::parseFileHeader(const QString &filePath, QStringList &symbols)
{
    // 初始化输入参数
    symbols.clear();

    // 打开文件
    QFile file(filePath);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        qCritical() << QStringLiteral("文件打开失败！[%1]").arg(filePath);
        return false;
    }

    //
    QTextStream ts(&file);
    ts.skipWhiteSpace();

    //
    if (ts.atEnd()) {
        qCritical() << QStringLiteral("文件[%1]中无数据").arg(filePath);
        return false;   // 无数据
    }

    // 读取信号行数据
    symbols = ts.readLine().trimmed().split(' ', QString::SkipEmptyParts);
    if (symbols.isEmpty()) {
        qCritical() << QStringLiteral("信号列表为空！[%1]").arg(filePath);
        return false;
    }

    // 去除信号两头空格
    QMutableStringListIterator iterSymbols(symbols);
    while (iterSymbols.hasNext()) {
        QString &symbol = iterSymbols.next();
        if (symbol.compare("TIME", Qt::CaseInsensitive) == 0) {
            symbol = "T";   // 如果时间信号名称为TIME等为SQL专用关键字时，须替换为非关键字名称
        } else {
            symbol = symbol.trimmed().replace('-', '_');
        }
    }

    return true;
}

bool JFlyRecord::parseFileBaseInfo(const QString &filePath, JFileBaseInfo &info)
{
    // 解析文件名称
    if (!parseFileName(filePath, info.nameInfo)) {
        return false;   // 失败
    }

    // 解析文件头信号
    if (!parseFileHeader(filePath, info.symbols)) {
        return false;   // 失败
    }

    info.filePath = filePath;

    return true;
}

bool JFlyRecord::parseFile(const QString &filePath, const QList<stSignalSiftInfo> &sifts,
                           bool isGlobalMode, bool isIgnoreSift)
{
    // 解析文件名称信息
    JFileNameInfo nameInfo;
    if (!parseFileName(filePath, nameInfo)) {
        return false;
    }

    // 获取文件中的所有信号量
    QStringList symbols;
    if (!parseFileHeader(filePath, symbols)) {
        return false;
    }

    //
    if (symbols.isEmpty()) {
        return false;
    }

    //
    stMainTableInfo mainTableInfo;
    mainTableInfo.JX = nameInfo.JX;
    mainTableInfo.JH = nameInfo.JH;
    mainTableInfo.RQ = nameInfo.minRQ;
    mainTableInfo.JC = nameInfo.JC;
    mainTableInfo.DYXHB = JSqlTableMgr::generateSignalTableName(nameInfo);
    mainTableInfo.DYZB = JSqlTableMgr::generateRecordTableName(nameInfo);
    mainTableInfo.DYRDFW = JSqlTableMgr::generateHotSpotTableName(nameInfo);

    // 根据条件过滤掉未被筛选的
    QStringList validSymbols = symbols;
    QList<stSignalInfo> signalInfos;
    foreach (const QString &symbol, symbols) {
        stSignalInfo signalInfo;
        signalInfo.YXHM = symbol;
        signalInfo.SSJX = nameInfo.JX;
        signalInfo.SSJH = nameInfo.JH;
        signalInfo.SSRQ = nameInfo.minRQ;
        signalInfo.SSJC = nameInfo.JC;
        // sift
        int index = sifts.indexOf(symbol);
        if (index != -1) {
            const stSignalSiftInfo &siftInfo = sifts.at(index);
            signalInfo.BM = siftInfo.BM;
            signalInfo.SXBZ = siftInfo.SXBZ;
            //
            if (!(isIgnoreSift || signalInfo.SXBZ)) {
                validSymbols.removeOne(symbol);
            }
        } else {
            //signalInfo.SXBZ = true;
        }
        //
        signalInfos.append(signalInfo);
    }

    //
    if (validSymbols.count() <= 1) {
        // 新导入的文件没有需要筛选中并导入的信号量（除时间外）
        return true;
    }

    // 数据表原信号名称
    QStringList oldSymbols;

    //
    JSqlTableMgr::instance()->transaction();

    //
    bool isEmptyTable = true;

    // 创建表
    if (JSqlTableMgr::instance()->existsTable(mainTableInfo.DYZB)) {
        // 获取数据表更新信号之前的所有信号名称（包括时间）
        oldSymbols = JSqlTableMgr::instance()->tableColumns(mainTableInfo.DYZB);
        // 对表更新新的信号量
        if (!JSqlTableMgr::instance()->updateRecordSymbol(mainTableInfo.DYZB,
                                                          validSymbols.mid(1),  // 去掉第一个时间信号量
                                                          isIgnoreSift)) {
            JSqlTableMgr::instance()->rollback();
            qCritical() << QStringLiteral("更新数据表[%1]信号量失败！").arg(mainTableInfo.DYZB);
            return false;
        }
        // 对时间信号量做特殊处理
        stSignalInfo &signalTimeInfo = signalInfos.first();
        if (!JSqlDataMgr::instance()->readSignalDataTime(mainTableInfo.DYXHB, signalTimeInfo)) {
            JSqlTableMgr::instance()->rollback();
            return false;
        }
        validSymbols.replace(0, signalTimeInfo.YXHM);
        //
        isEmptyTable = false;
    } else {
        // 初始化数据表原信号名称序列
        oldSymbols = validSymbols;
        // 增加一条主表数据
        if (!JSqlDataMgr::instance()->addMainTableData(mainTableInfo)) {
            JSqlTableMgr::instance()->rollback();
            qCritical() << QStringLiteral("增加一条主表数据失败！");
            return false;
        }

        // 创建对应信号表
        if (!JSqlDataMgr::instance()->createSignalTable(mainTableInfo.DYXHB)) {
            JSqlTableMgr::instance()->rollback();
            qCritical() << QStringLiteral("创建信号表失败！[%1]").arg(mainTableInfo.DYXHB);
            return false;
        }

        // 创建对应值表
        if (!JSqlDataMgr::instance()->createRecordTable(mainTableInfo.DYZB, validSymbols)) {
            JSqlTableMgr::instance()->rollback();
            qCritical() << QStringLiteral("创建数据记录表失败！[%1]").arg(mainTableInfo.DYZB);
            return false;
        }

        // 创建对应热点范围表
        if (!JSqlDataMgr::instance()->createHotSpotTable(mainTableInfo.DYRDFW)) {
            JSqlTableMgr::instance()->rollback();
            qCritical() << QStringLiteral("创建热点范围表失败！[%1]").arg(mainTableInfo.DYRDFW);
            return false;
        }

        // 对时间信号量做特殊处理
        stSignalInfo &signalTimeInfo = signalInfos.first();
        signalTimeInfo.SXBZ = true;
        if (isGlobalMode) {
            signalTimeInfo.BM = JRuleDict::instance()->nameOfSymbol(signalTimeInfo.YXHM);
        } else {
            signalTimeInfo.BM = JRuleDict::instance()->nameOfSymbol(nameInfo.JX, signalTimeInfo.YXHM);
        }
    }

    // 保存（更新）对应信号表数据
    if (!JSqlDataMgr::instance()->addSignalData(mainTableInfo.DYXHB, signalInfos, isIgnoreSift)) {
        JSqlTableMgr::instance()->rollback();
        return false;
    }

    //
    JSqlTableMgr::instance()->commit();

    // 保存（更新）对应值表数据
    if (!parseRecords(filePath, mainTableInfo.DYZB, oldSymbols, validSymbols, signalInfos,
                      isIgnoreSift, isEmptyTable)) {
        return false;
    }

    return true;
}

bool JFlyRecord::parseFile(const QStringList &filePaths, const QList<stSignalSiftInfo> &sifts,
                           bool isGlobalMode, bool isIgnoreSift)
{
    foreach (const QString &filePath, filePaths) {
        if (!parseFile(filePath, sifts, isGlobalMode, isIgnoreSift)) {
            // failure
        }
    }

    return true;
}

bool JFlyRecord::classifyFile(const QStringList &filePaths, QList<QStringList> &classifiedFilePaths)
{
    // 初始化输入参数
    classifiedFilePaths.clear();

    // 文件分类
    QMap<QString/*pk*/, QStringList/*filePaths*/> mapFilePaths;
    QStringListIterator citerFilePaths(filePaths);
    while (citerFilePaths.hasNext()) {
        const QString &filePath = citerFilePaths.next();
        QString fileName = QFileInfo(filePath).fileName();
        if (!exactMatchFileName(fileName)) {
            continue;       // 文件名称格式错误
        }
        // 存储
        mapFilePaths[fileName.section('_', 0, 0)].append(filePath);
    }

    // 获取分类结果
    classifiedFilePaths = mapFilePaths.values();

    return true;
}

QString JFlyRecord::fileNamePattern()
{
    return QStringLiteral("(\\w){3}(\\w)+[-](\\d)+[-]\\d[_][^/\\\\]*");
}

bool JFlyRecord::exactMatchFileName(const QString &fileName)
{
    return QRegExp(fileNamePattern()).exactMatch(fileName);
}

bool JFlyRecord::readSignalMHSection(const JFileNameInfo &info,
                                     QString &machSection,
                                     QString &heightSection) const
{
    Q_D(const JFlyRecord);
    return d->dataMgr->readSignalAxisSection(info,
                                             QStringLiteral("马赫数"),
                                             QStringLiteral("高度"),
                                             machSection, heightSection);
}

bool JFlyRecord::readSignalMHSection(const QString &tableName,
                                     QString &machSection,
                                     QString &heightSection) const
{
    Q_D(const JFlyRecord);
    return d->dataMgr->readSignalAxisSection(tableName,
                                             QStringLiteral("马赫数"),
                                             QStringLiteral("高度"),
                                             machSection, heightSection);
}

bool JFlyRecord::readSignalMHName(const JFileNameInfo &info,
                                  const QString &machSection,
                                  const QString &heightSection,
                                  QString &machName,
                                  QString &heightName) const
{
    Q_D(const JFlyRecord);
    return d->dataMgr->readSignalAxisName(info, machSection, heightSection,
                                          machName, heightName);
}

bool JFlyRecord::readSignalMHName(const QString &tableName,
                                  const QString &machSection,
                                  const QString &heightSection,
                                  QString &machName,
                                  QString &heightName) const
{
    Q_D(const JFlyRecord);
    return d->dataMgr->readSignalAxisName(tableName, machSection, heightSection,
                                          machName, heightName);
}

bool JFlyRecord::samplingRecordData(const QString &suffix,
                                    int interval)
{
    if (interval <= 0) {
        return false;
    }

    //
    const QString recordTableName = JSqlDataMgr::instance()->recordTableNamePrefix().append(suffix);

    //
    int count = JSqlTableMgr::instance()->rowCountOfTable(recordTableName);

    //
    if (interval >= count) {
        return true;
    }
    //
    QStringList columnNames = JSqlTableMgr::instance()->tableColumns(recordTableName);
    if (columnNames.isEmpty()) {
        return false;
    }

    //
    sqlite3* db;
    int result = SQLITE_OK;

    // 打开数据库
    result = sqlite3_open(JSqlTableMgr::instance()->databaseName().toUtf8().data(), &db);
    if (result != SQLITE_OK) {
        qCritical() << "数据库打开失败：" << QString(sqlite3_errstr(result));
        return false;
    }

    // 启用事务
    sqlite3_exec(db, "begin;", 0, 0, 0);

    //
    sqlite3_stmt *stmtDelete;
    QString queryDelete;

    // 删除数据语句预处理
    queryDelete = QString("delete from %1 where rowid in "
                          "(select rowid from %1 order by %2 limit ?,%3)")
            .arg(recordTableName).arg(columnNames.first()).arg(interval);
    result = sqlite3_prepare_v2(db, queryDelete.toUtf8().data(), -1, &stmtDelete, 0);
    if (result != SQLITE_OK) {
        qCritical() << QString(sqlite3_errstr(result));
        sqlite3_exec(db, "rollback;", 0, 0, 0);
        sqlite3_close(db);
        return false;
    }

    //
    int newCount = count / interval;   // 间隔采样
    for (int i = 1; i <= newCount; i++) {
        // 复位存储过程
        sqlite3_reset(stmtDelete);
        // 绑定行偏移量
        sqlite3_bind_int(stmtDelete, 1, i);
        // 执行准备语句
        sqlite3_step(stmtDelete);
    }

    //
    sqlite3_finalize(stmtDelete);

    //
    sqlite3_exec(db, "commit;", 0, 0, 0);

    //
#if 1
    sqlite3_exec(db, QString("vacuum %1;").arg(recordTableName).toUtf8().data(), 0, 0, 0);
#endif
    //
    sqlite3_close(db);

    return true;
}

bool JFlyRecord::parseRecords(const QString &filePath,
                              const QString &tableName,
                              const QStringList &oldSymbols,
                              const QStringList &validSymbols,
                              const QList<stSignalInfo> &signalInfos,
                              bool ignoreSift, bool isEmptyTable)
{
    Q_D(JFlyRecord);

    //
    QFile file(filePath);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        qCritical() << QStringLiteral("文件[%1]打开失败！").arg(filePath);
        return false;
    }

    //
    QTextStream ts(&file);
    ts.skipWhiteSpace();

    //
    if (ts.atEnd()) {
        qWarning() << QStringLiteral("文件[%1]中无数据！").arg(filePath);
        return false;   // 无数据
    }

    // 略过信号行
    ts.readLine();

    //
    if (ts.atEnd()) {
        qWarning() << QStringLiteral("文件[%1]中无数据！").arg(filePath);
        return false;   // 无数据
    }

    //
    QSqlDatabase database = *reinterpret_cast<QSqlDatabase *>(d->dataMgr->database());

    //
    sqlite3* db;
    int result = SQLITE_OK;

    // 打开数据库
    result = sqlite3_open(database.databaseName().toUtf8().data(), &db);
    if (result != SQLITE_OK) {
        qCritical() << "数据库打开失败：" << QString(sqlite3_errstr(result));
        sqlite3_exec(db, "rollback;", 0, 0, 0);
        sqlite3_close(db);
        return false;
    }

    //JPreciTimer ptimer;
    //ptimer.reset();

    //
    //d->db.exec("PRAGMA cache_size=8000");

    // 启用事务
    sqlite3_exec(db, "begin;", 0, 0, 0);

    //
    int i = 0, j = 0, count = 0;
    QString line;
    std::string stime;
    sqlite3_stmt *stmtInsert = 0, *stmtUpdate = 0, *stmtFind = 0,
            *stmtRead = 0, *stmtUpdateOld = 0, *stmtUpdateNew = 0;
    QString queryInsert, queryUpdate, queryFind, queryRead, queryUpdateOld, queryUpdateNew;

    // 插入数据语句预处理
    queryInsert = QString("insert into %1(%2) values(%3)")
            .arg(tableName).arg(validSymbols.join(','))
            .arg(QString(validSymbols.count(), '?')
                 .split("", QString::SkipEmptyParts).join(','));
    result = sqlite3_prepare_v2(db, queryInsert.toUtf8().data(), -1, &stmtInsert, 0);
    if (result != SQLITE_OK) {
        qCritical() << QString(sqlite3_errstr(result));
        sqlite3_exec(db, "rollback;", 0, 0, 0);
        sqlite3_close(db);
        return false;
    }
    // 更新数据语句预处理
    queryUpdate = QString("update %1 set %3=? where %2=?")
            .arg(tableName).arg(validSymbols.first())
            .arg(QStringList(validSymbols.mid(1)).join("=?,"));
    result = sqlite3_prepare_v2(db, queryUpdate.toUtf8().data(), -1, &stmtUpdate, 0);
    if (result != SQLITE_OK) {
        qCritical() << QString(sqlite3_errstr(result));
        sqlite3_exec(db, "rollback;", 0, 0, 0);
        sqlite3_close(db);
        return false;
    }

    //========================================================

    //
    QStringList tOldSymbols = oldSymbols;
    QStringList tNewSymbols = validSymbols;
    tOldSymbols.removeFirst();  // 去掉时间信号
    tNewSymbols.removeFirst();  // 去掉时间信号

    //
    int oldCount = 0;
    int newCount = 0;
    int allCount = 0;
    QVector<double> v1, v2;

    // 避免对同一个文件信号量相同的多次执行复杂导入导致的低效率
    if (isEmptyTable || validSymbols.count() == 1) {
        //
    } else {
        //
        QStringListIterator citrOldSymbols(oldSymbols);
        while (citrOldSymbols.hasNext()) {
            const QString &oldSymbol = citrOldSymbols.next();
            if (validSymbols.contains(oldSymbol)) {
                tOldSymbols.removeOne(oldSymbol);
            }
        }

        oldCount = tOldSymbols.count();
        newCount = tNewSymbols.count();
        allCount = oldCount + newCount;
        v1.resize(oldCount);
        v2.resize(newCount);

        // 空白填充
        queryRead = QString("select %3 from %1 order by %2 limit 1")
                .arg(tableName).arg(validSymbols.first())
                .arg(tOldSymbols.join(','));
        result = sqlite3_prepare_v2(db, queryRead.toUtf8().data(), -1, &stmtRead, 0);
        if (result != SQLITE_OK) {
            qCritical() << QString(sqlite3_errstr(result));
            sqlite3_exec(db, "rollback;", 0, 0, 0);
            sqlite3_close(db);
            return false;
        }

        //
        if (sqlite3_step(stmtRead) == SQLITE_ROW) {
            for (i = 0; i < oldCount; ++i) {
                v1[i] = sqlite3_column_double(stmtRead, i);
            }
        }

        //
        sqlite3_finalize(stmtRead);
        stmtRead = 0;
    }

    //========================================================

    // 查找数据语句预处理
    queryFind = QString("select count() from %1 where %2=?")
            .arg(tableName).arg(validSymbols.first());
    result = sqlite3_prepare_v2(db, queryFind.toUtf8().data(), -1, &stmtFind, 0);
    if (result != SQLITE_OK) {
        qCritical() << QString(sqlite3_errstr(result));
        sqlite3_exec(db, "rollback;", 0, 0, 0);
        sqlite3_close(db);
        return false;
    }
    // 数据解析和入库

    //
    bool isNoReadFirstRow = true;
    while (!ts.atEnd()) {// 读取一行数据
        line = ts.readLine().trimmed();
        if (line.isEmpty()) {
            continue;
        }
        // 分离数据项
        QStringList values = line.split(' ', QString::SkipEmptyParts);
        if (isNoReadFirstRow) {
            count = qMin(values.count(), newCount);
            for (i = 1; i < count; ++i) {
                v2[i - 1] = values.at(i).toDouble();
            }
            isNoReadFirstRow = false;
        }
        // 获取时间值
        stime = values.first().toStdString();
        // 获取需要存储的列数
        count = qMin(values.count(), signalInfos.count());
        //
        sqlite3_reset(stmtFind);
        //
        sqlite3_bind_text(stmtFind, 1, stime.c_str(), -1, 0);
        //
        if (sqlite3_step(stmtFind) != SQLITE_ROW) {
            continue;   // error
        }
        //
        if (sqlite3_column_int(stmtFind, 0) > 0) {  // 找到。更新数据
            // 复位存储过程
            sqlite3_reset(stmtUpdate);
            if (ignoreSift) {
                for (i = 1; i < count; ++i) {
                    sqlite3_bind_double(stmtUpdate, i, values[i].toDouble());
                }
                // 绑定时间值
                sqlite3_bind_text(stmtUpdate, i, stime.c_str(), -1, 0);
            } else {
                for (i = 1, j = 1; i < count; ++i) {
                    if (signalInfos[i].SXBZ) {
                        sqlite3_bind_double(stmtUpdate, j++, values[i].toDouble());
                    }
                }
                // 绑定时间值
                sqlite3_bind_text(stmtUpdate, j, stime.c_str(), -1, 0);
            }
            // 执行准备语句
            sqlite3_step(stmtUpdate);
        } else {    // 未找到。插入数据
            // 复位存储过程
            sqlite3_reset(stmtInsert);
            // 绑定时间值
            sqlite3_bind_text(stmtInsert, 1, stime.c_str(), -1, 0);
            if (ignoreSift) {
                for (i = 1; i < count; ++i) {
                    sqlite3_bind_double(stmtInsert, i + 1, values[i].toDouble());
                }
            } else {
                for (i = 1, j = 2; i < count; ++i) {
                    if (signalInfos[i].SXBZ) {
                        sqlite3_bind_double(stmtInsert, j++, values[i].toDouble());
                    }
                }
            }
            // 执行准备语句
            sqlite3_step(stmtInsert);
        }
    }
    //
    sqlite3_finalize(stmtInsert);
    sqlite3_finalize(stmtUpdate);
    sqlite3_finalize(stmtFind);

    // 避免对同一个文件信号量相同的多次执行复杂导入导致的低效率
    if (isEmptyTable || validSymbols.count() == 1) {
        //
    } else {
        // 更新数据语句预处理
        queryUpdateOld = QString("update %1 set %3=? where %2=?")
                .arg(tableName).arg(validSymbols.first())
                .arg(tOldSymbols.join("=?,"));
        result = sqlite3_prepare_v2(db, queryUpdateOld.toUtf8().data(), -1, &stmtUpdateOld, 0);
        if (result != SQLITE_OK) {
            qCritical() << QString(sqlite3_errstr(result));
            sqlite3_exec(db, "rollback;", 0, 0, 0);
            sqlite3_close(db);
            return false;
        }
        // 更新数据语句预处理
        queryUpdateNew = QString("update %1 set %3=? where %2=?")
                .arg(tableName).arg(validSymbols.first())
                .arg(tNewSymbols.join("=?,"));
        result = sqlite3_prepare_v2(db, queryUpdateNew.toUtf8().data(), -1, &stmtUpdateNew, 0);
        if (result != SQLITE_OK) {
            qCritical() << QString(sqlite3_errstr(result));
            sqlite3_exec(db, "rollback;", 0, 0, 0);
            sqlite3_close(db);
            return false;
        }

        //
        QStringList symbols = tOldSymbols + tNewSymbols;

        // 空白填充
        queryRead = QString("select %2,%3 from %1 order by %2")
                .arg(tableName).arg(validSymbols.first())
                .arg(symbols.join(','));
        result = sqlite3_prepare_v2(db, queryRead.toUtf8().data(), -1, &stmtRead, 0);
        if (result != SQLITE_OK) {
            qCritical() << QString(sqlite3_errstr(result));
            sqlite3_exec(db, "rollback;", 0, 0, 0);
            sqlite3_close(db);
            return false;
        }

        //
        while (sqlite3_step(stmtRead) == SQLITE_ROW) {
            //
            stime = std::string((const char *)sqlite3_column_text(stmtRead, 0));
            //
            const unsigned char *s1 = sqlite3_column_text(stmtRead, 1);
            const unsigned char *s2 = sqlite3_column_text(stmtRead, 1 + oldCount + newCount - 1);
            if (s1 == 0) {   // 旧数据为空
                //
                sqlite3_reset(stmtUpdateOld);
                for (i = 1; i <= oldCount; ++i) {
                    //
                    sqlite3_bind_double(stmtUpdateOld, i, v1[i - 1]);
                }
                //
                sqlite3_bind_text(stmtUpdateOld, i, stime.c_str(), -1, 0);
                //
                result = sqlite3_step(stmtUpdateOld);
                if (result != SQLITE_DONE) {
                    Q_ASSERT(false);
                }
                //
                for (; i <= allCount; ++i) {
                    v2[i - oldCount - 1] = sqlite3_column_double(stmtRead, i);
                }
                //
            } else if (s2 == 0) {
                //
                for (i = 1; i <= oldCount; ++i) {
                    v1[i - 1] = sqlite3_column_double(stmtRead, i);
                }
                //
                sqlite3_reset(stmtUpdateNew);
                //
                for (i = 1; i <= newCount; ++i) {
                    sqlite3_bind_double(stmtUpdateNew, i, v2[i - 1]);
                }
                //
                sqlite3_bind_text(stmtUpdateNew, i, stime.c_str(), -1, 0);
                //
                result = sqlite3_step(stmtUpdateNew);
                if (result != SQLITE_DONE) {
                    Q_ASSERT(false);
                }
            } else {
                //
                for (i = 1; i <= oldCount; ++i) {
                    v1[i - 1] = sqlite3_column_double(stmtRead, i);
                }
                //
                for (; i <= allCount; ++i) {
                    v2[i - oldCount - 1] = sqlite3_column_double(stmtRead, i);
                }
            }
        }
        //
        sqlite3_finalize(stmtRead);
        sqlite3_finalize(stmtUpdateOld);
        sqlite3_finalize(stmtUpdateNew);
    }

    //
    sqlite3_exec(db, "commit;", 0, 0, 0);

    //
#if 0
    sqlite3_exec(db, QString("vacuum %1;").arg(tableName).toUtf8().data(), 0, 0, 0);
#endif
    //
    sqlite3_close(db);

    return true;
}

#if 1

// - class JFlyRecordGeneraterPrivate -

class JFlyRecordGeneraterPrivate
{
    J_DECLARE_PUBLIC(JFlyRecordGenerater)
    public:
        JFlyRecordGeneraterPrivate(JFlyRecordGenerater *parent)
      : q_ptr(parent)
    {

    }

private:
};

// - class JFlyRecordGenerater -

J_IMPLEMENT_SINGLE_INSTANCE(JFlyRecordGenerater)

bool JFlyRecordGenerater::generateFlyRecordFile(const QString &filePath,
                                                const QStringList &symbols,
                                                long rowCount)
{
    QFile file(filePath);
    if (!file.open(QFile::WriteOnly | QFile::Text)) {
        return false;
    }

    QTextStream ts(&file);

    // symbols
    ts << '\n'
       << symbols.join(QString(4, ' '))
       << '\n';

    //
    qsrand((uint)QDateTime::currentDateTime().toMSecsSinceEpoch());

    //
    int count = symbols.count();
    QTime time = QTime(0, 0, 0, 10);
    for (int i = 0; i < rowCount; ++i) {
        time = time.addMSecs(1);
        ts << time.toString("hh:mm:ss:zzz").append("    ");
        qreal y = qSin(M_PI / 100 * i) * 1000 + qrand() % 100;
        ts << QString("%1").arg(i, 8, 10, QChar(' ')); // height
        ts << QString("%1").arg(i + 1, 8, 10, QChar(' ')); // width
        for (int j = 3; j < count; ++j) {
            ts << QString("%1").arg(y + qrand() % 50, 8, 'g', 4, QChar(' '));
        }
        ts << '\n';
    }

    return true;
}

JFlyRecordGenerater::JFlyRecordGenerater() :
    d_ptr(new JFlyRecordGeneraterPrivate(this))
{

}

JFlyRecordGenerater::~JFlyRecordGenerater()
{
    delete d_ptr;
}

#endif
